# coding: utf-8

# 回调

"""操作系统已经把IO状态的改变封装成了事件， 如可读，可写的的事件，并且可以为这些事件绑定
处理函数，所以我们可以使用这种方法，为socket的IO状态绑定处理函数，交给系统进行调动，
这样就是回调方式，python的select模块支持这样的操作。
"""
import socket
from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
import sys
selector = DefaultSelector()
stopped = False
urls_todo = []


class Crawler:
    def __init__(self, url):
        self.url = url
        self.sock = None
        self.response = b""

    def fetch(self):
        self.sock = socket.socket()
        self.sock.setblocking(False)
        try:
            self.sock.connect(("www.baidu.com", 80))
        except BlockingIOError:
            pass
        selector.register(self.sock.fileno(), EVENT_WRITE, self.connected)

    def connected(self, key, mask):
        selector.unregister(key.fd)
        get = 'GET {0} HTTP/1.0\r\nHost: www.baidu.com\r\n\r\n'.format(self.url)
        self.sock.send(get.encode("ascii"))
        selector.register(key.fd, EVENT_READ, self.read_response)

    def read_response(self, key, mask):
        global stopped
        # 如果响应大于4KB，下一次循环 会继续读
        chunk = self.sock.recv(4096)
        if chunk:
            self.response += chunk
        else:
            selector.unregister(key.fd)
            urls_todo.remove(self.url)
            if not urls_todo:
                stopped = True


def loop():
    while not stopped:
        # 阻塞， 直到一个事件发生
        events = selector.select()
        for event_key, event_mask in events:
            callback = event_key.data
            callback(event_key, event_mask)


if __name__ == "__main__":
    import time
    start = time.time()
    for i in range(int(sys.argv[1])):
        urls_todo.append("/" + str(i))
        crawler = Crawler("/" + str(i))
        crawler.fetch()
    loop()
    print("spend time : %s" % (time.time() - start))









